#!/bin/bash
#
# Copyright (C) 2017 Francis Deslauriers <francis.deslauriers@efficios.com>
#
# SPDX-License-Identifier: GPL-2.0-only
#

TEST_DESC="Kernel tracer - Callstack context"

CURDIR=$(dirname "$0")/
TESTDIR=$CURDIR/../..
NUM_TESTS=12
TEST_APP_USERSPACE="$TESTDIR/utils/testapp/gen-syscall-events-callstack/gen-syscall-events-callstack"
TEST_APP_KERNELSPACE="$TESTDIR/utils/testapp/gen-syscall-events/gen-syscall-events"
PARSE_CALLSTACK="$TESTDIR/utils/parse-callstack.py"

SESSION_NAME="callstack"
CHANNEL_NAME="chan0"

source "$TESTDIR/utils/utils.sh"

function lttng_untrack_all()
{
	lttng_untrack 0 "-s $SESSION_NAME --all --pid -k"
}

function lttng_track_pid()
{
	local PID=$1
	lttng_track 0 "-s $SESSION_NAME -k --pid=$PID"
}

function run_workload()
{
	local TEST_APP=$1
	# shift the first argument, passing along the other args if any to the
	# test app.
	shift
	local start_file_sync=$(mktemp -u -t "tmp.${FUNCNAME[0]}_sync_before_first.XXXXXX")

	lttng_untrack_all

	./"$TEST_APP" "$start_file_sync" "$@" &
	PID=$!
	lttng_track_pid $PID

	start_lttng_tracing_ok

	# Create start file to launch the execution of the syscall call by the
	# test app.
	touch "$start_file_sync"

	wait $PID

	stop_lttng_tracing_ok

	# Clean up the synchronization file.
	rm -f "$start_file_sync"
}

function test_user_callstack()
{
	TRACE_PATH=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
	# This is the expected userspace callstack. (see gen-syscall-events-callstack.c)
	USER_CS_EXPECTED="main fct_a fct_b fct_c my_gettid"
	EVENT_NAME="gettid"

	diag "Userspace callstack test"
	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	lttng_enable_kernel_channel_ok "$SESSION_NAME" "$CHANNEL_NAME"

	lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME"
	add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-user"

	run_workload $TEST_APP_USERSPACE

	destroy_lttng_session_ok "$SESSION_NAME"

	"_run_babeltrace_cmd" "$TRACE_PATH" | grep $EVENT_NAME | ./"$PARSE_CALLSTACK" --user "$TEST_APP_USERSPACE" $USER_CS_EXPECTED
	ok $? "Validate userspace callstack"

	rm -rf "$TRACE_PATH"
}

function test_kernel_callstack()
{
	TRACE_PATH=$(mktemp -d -t "tmp.${FUNCNAME[0]}_trace_path.XXXXXX")
	# Those are symbol expected to be present in the kernel callstack. This
	# is not an exhaustive list since it's kernel dependent.

	# FIXME: we used to test for the following symbols as well:
	# save_stack_trace, lttng_callstack_get_size, but they were removed
	# because:
	#	1. kernel commit 77072f09 make it so that save_stack_trace is
	#	 omitted from the callstack itself, and
	#
	#	2. the code (of this commit) can trigger Tail Call Optimization
	#	which mess up with the stacktrace by omiting the wrong address
	#	from the stacktrace.
	# When this is fixed, we should add both save_stack_trace and
	# lttng_callstack_get_size symbols back in the list of expected
	# addresses.
	KERNEL_CS_EXPECTED="lttng_event_reserve"
	EVENT_NAME="read"

	diag "Kernel callstack test"
	create_lttng_session_ok $SESSION_NAME "$TRACE_PATH"
	lttng_enable_kernel_channel_ok "$SESSION_NAME" "$CHANNEL_NAME"

	lttng_enable_kernel_syscall_ok "$SESSION_NAME" "$EVENT_NAME" "$CHANNEL_NAME"
	add_context_kernel_ok "$SESSION_NAME" "$CHANNEL_NAME" "callstack-kernel"

	run_workload "$TEST_APP_KERNELSPACE" "/proc/cpuinfo" "/proc/cmdline"

	destroy_lttng_session_ok "$SESSION_NAME"

	"_run_babeltrace_cmd" "$TRACE_PATH" | grep $EVENT_NAME | ./"$PARSE_CALLSTACK" --kernel $KERNEL_CS_EXPECTED
	ok $? "Validate kernel callstack"

	rm -rf "$TRACE_PATH"
}

# Only run userspace callstack test on x86 (32 and 64 variants)
uname -m | grep "86" >/dev/null 2>&1
if test $? == 0; then
	NUM_TESTS=$((NUM_TESTS+10))
	RUN_USERSPACE_TEST=1
else
	RUN_USERSPACE_TEST=0
fi

# MUST set TESTDIR before calling those functions
plan_tests $NUM_TESTS

print_test_banner "$TEST_DESC"

bail_out_if_no_babeltrace

check_skip_kernel_test "$NUM_TESTS" "Skipping all tests." ||
{
	validate_lttng_modules_present
	start_lttng_sessiond

	if test $RUN_USERSPACE_TEST == 1; then
		test_user_callstack
	fi

	test_kernel_callstack

	stop_lttng_sessiond
}
